---
title: Million Lint is in public beta
date: FEB 29, 2024
description: We couldn't wait to share it with you!
---

import { Steps, Callout, Tabs, Tab } from 'nextra-theme-docs';
import Image from 'next/image';

<div className="flex flex-col items-center gap-2 pt-8">

<Image src="/lint/logo.png" alt="Million Lint" height={520} width={1200} />

# Million Lint is in public beta

<small>[Aiden Bai](https://twitter.com/aidenybai), [John Yang](https://twitter.com/fiveseveny), [Nisarg Patel](https://twitter.com/nisargptel) – February 29, 2024</small>

</div>

---

It’s launch time. After three months and hundreds of commits, we invite you to try out **Million Lint**. The experience is not finished – there are a few known bugs and several missing features – but we are really happy with how it's shaping up and couldn't wait to share it with you.

Get started in one command by running this in any React app:

```bash copy
npx @million/lint@latest
```

<details>
  <summary>
    Set up failed? Click here to follow the guide.
  </summary>

<Steps>

### Install NPM package

<Tabs items={['npm', 'pnpm', 'yarn', 'bun']} storageKey="selected-pkg-manager">
  {/* prettier-ignore */}
  <Tab>
  ```bash copy
  npm install @million/lint@latest
  ```
  </Tab>
  {/* prettier-ignore */}
  <Tab>
  ```bash copy
  pnpm install @million/lint@latest
  ```
  </Tab>
  {/* prettier-ignore */}
  <Tab>
  ```bash copy
  yarn add @million/lint@latest
  ```
  </Tab>
  {/* prettier-ignore */}
  <Tab>
  ```bash copy
  bun add @million/lint@latest
  ```
  </Tab>
</Tabs>

### Install VSCode extension

Go to the [VSCode Marketplace](https://marketplace.visualstudio.com/items?itemName=million.million-lint) and install the Million Lint extension.

### Add the compiler to your application

<Tabs items={['Next.js', 'Astro', 'Gatsby', 'Vite','Remix', 'Create React App', 'Webpack', 'Rollup']} storageKey="selected-bundler-compiler">
<Tab>
Million.js is supported within the `/app` ("use client" components only) and `/pages`

```js filename="next.config.mjs"
import MillionCompiler from '@million/lint';

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
};

export default MillionCompiler.next({ 
  rsc: true // if used in the app router mode
})(nextConfig);
```
</Tab>

<Tab>
```js filename="astro.config.mjs"
import { defineConfig } from "astro/config";
import MillionCompiler from "@million/lint";

export default defineConfig({
   vite: {
     plugins: [MillionCompiler.vite()],
   },
});
```
</Tab>

<Tab>
```js filename="gatsby-node.js"
const MillionCompiler = require("@million/lint");

exports.onCreateWebpackConfig = ({ actions }) => {
  actions.setWebpackConfig({
    plugins: [MillionCompiler.webpack()],
  });
};
````
</Tab>

<Tab>
```js filename="vite.config.js"
import MillionCompiler from "@million/lint";
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [MillionCompiler.vite(), react()],
});
````
</Tab>

<Tab>
```js filename="vite.config.js"
import { unstable_vitePlugin as remix } from "@remix-run/dev";
import MillionCompiler from "@million/lint";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [MillionCompiler.vite(), remix()],
});
```
</Tab>

<Tab>
  <Callout type="warning">
    If you are using a [Create React App (CRA)](https://create-react-app.dev/) Setup, you will need to [configure Craco](https://craco.js.org/docs/getting-started/) before proceeding.
  </Callout>

```js filename="craco.config.js"
const MillionCompiler = require('@million/lint');

module.exports = {
  plugins: [MillionCompiler.craco({ legacyHmr: true })],
};
```
</Tab>

<Tab>
```js filename="webpack.config.js"
const MillionCompiler = require("@million/lint");

module.exports = {
  plugins: [MillionCompiler.webpack()],
};
```
</Tab>

<Tab>
```js filename="rollup.config.js"
import MillionCompiler from "@million/lint";

export default {
  plugins: [MillionCompiler.rollup()],
};
```
</Tab>
</Tabs>

### Run your app!

</Steps>

</details>

Let us know if you have any [questions or feedback](https://million.dev/chat)!

## What is Million Lint?

Million Lint is a VSCode extension that keeps your React website fast. We identify slow code and provide suggestions to fix it. It’s like ESLint, but for performance!

Many developers try to use tools like React Devtools to find unnecessary renders. Unfortunately, most lack the knowledge to properly manage the complexity.

See the difference between React Devtools and Million Lint:

<Tabs items={['React Devtools', 'Million Lint']}>

<Tab>
_Feeling lost? Yeah, us too (+99% of all devs)_

![React Profiler Flamegraph](/lint/profiler.jpeg)

</Tab>

<Tab>
_Get profile information within VSCode!_

<div className="pt-4">
<video src="/lint/renders.mp4" controls autoPlay />
</div>

</Tab>

</Tabs>

Complicated tools means developers give up. Every developer knows this experience: we insert `console.log` everywhere, catch some promising leads, but nothing happens before "time runs out." Eventually, the slow/buggy code never gets fixed, problems pile up on a backlog, and our end users are hurt.

Are there no better ways to surface performance issues?

### You vs. profilers

Existing instrumentation tools (Sentry, Datadog, etc.) show you the “entire universe” of web data. This can include every function call, network request, user interaction, core web vital, screenshot, memory … a dizzying amount of information to process. As such, these tools have interfaces that are overwhelming.

As for React & Chrome Devtools, they are simply unusable if you don't know what you are doing. And even if you do, it is hard to tell which part of the component re-rendered, or which hook or prop caused this change. Unless you are an expert, the current experience of performance debugging is hostile to most developers.

### Compiler or runtime?

JavaScript compilers enable us to perform static analysis on source code. For performance optimization, static analysis is great for breadth – by writing rules, developers can surface problems across the entire code base. This is why ESLint is so great. The tradeoff is that we can't predict how slow an operation will be without actually running it, making it impossible to implement Million Lint solely as a compiler.

Another approach is thus instrumenting at runtime. The advantage here is we can directly run the app and get rendering information (via the [React Fiber](https://blog.logrocket.com/deep-dive-react-fiber/)), so many of the complexity challenges with static analysis are instantly solved. However, the tradeoff with runtime is that you don’t have the original source (unless you use source maps, which are painful and slow). "Which hook or prop caused this change?" "Which part of the component re-rendered?" We couldn’t answer these questions without the source code.

### In search of a better way

So we went back to the drawing board. For Million Lint, we asked ourselves: how can we have the best of both worlds? We needed to create an profiling experience that didn’t hog build time or runtime; then, support a debugging flow that helped developers find performance problems fast.

Our answer is dynamic analysis – using both static analysis and runtime analysis where it makes sense. Instead of filtering top-down, we follow the necessary data flow to understand each component render and build up from there. This resulted in our custom instrumentation library, designed specifically for React apps.

## _import MillionCompiler from '@million/lint';_

The Million Lint experience starts with a compiler running dynamic analysis on individual React components.

First, the compiler inject handlers:

<Tabs items={['Compiled', 'Raw source']}>
<Tab>
```jsx /Million.capture/
function App({ start }) {

  Million.capture({ start });  // ✨ inject
  const [count, setCount] = Million.capture(useState)(start);  // ✨ inject

  useEffect(
    () => {
      console.log("double: ", count * 2);
    },
    Million.capture([count]),  // ✨ inject
  );

  return Million.capture(  // ✨ inject
    <Button onClick={() => setCount(count + 1)}>{count}</Button>,
  );
}
```
</Tab>
<Tab>
```jsx
function App({ start }) {

  const [count, setCount] = useState(start);

  useEffect(() => {
    console.log('double: ', count * 2);
  }, [count]);

  return <Button onClick={() => setCount(count + 1)}>{count}</Button>;
}
```

</Tab>
</Tabs>

During runtime, these inject handlers capture render, timing, and metadata information as you interact with your web app in dev mode. With this approach, we can get runtime profiling data from running the app, while keeping access to the source code thanks to the JavaScript compiler!

```jsx
// psuedo code of collected data:
[
  {
    kind: 'state',
    count: 7,
    time: 0.1,
    changes: [{ prev: 0, next: 8 }, ...],
  },
  // and so on...
];
```

After capturing the renders, we extend the compiler to asynchronously collect bundle, network, and state manager information without build time overhead. These real-time insights are then fed into the VSCode extension, where you can see the collected information and suggestions to fix it.

<Tabs items={['Render information', 'Bundle sizes']}>

<Tab>

<video src="/lint/renders.mp4" controls autoPlay />

</Tab>

<Tab>

<video src="/lint/bundles.mp4" controls autoPlay />

</Tab>

</Tabs>

Lastly, we made **Lint++**, which feeds the collected information into a language model to discover optimization opportunities. So even if you don’t know what you’re doing – Million Lint will show you how!

<div className="pt-6">
<video src="/lint/lint-plus-plus.mp4" controls />
</div>

## How good is Lint++?

Measuring the objective quality of **Lint++** requires a non-trivial dataset, so we are working on an open source React web app optimization benchmark for code-gen models (coming soon!). For now, we have tested **Lint++** on a dozen in-production products between our friends. For a public example, **Lint++** was able to identify and suggest the correct fix to every problem in [a slow, educational React app](https://github.com/3perf/react-workshop-ra) our friend [Ivan Akulov](https://twitter.com/iamakulov) built.

After running **Lint++** on Ivan's `notes` app, it labeled the following problems across 6 components:

- Unstable reference as prop optimization
- Memoizing expensive components
- Caching inline functions
- Suggesting virtualization
- Suggesting `use-context-selector` > `useContext`
- Moving context providers up the tree

We invited an engineer who's never seen the code base before to try speed up the `notes` app. Using Million Lint, he 3x'd the performance of the main page after ~15 minutes of work.

<div className="pt-6">
<video src="/lint/comparison.mp4" controls />
</div>

Of course, `notes` is a small app, and Million Lint wouldn't always find the best fix for every problem. We are working hard to improve its quality – try and please let us know about your experience!

## How will we make money from this?

In the next few weeks, we will open source the Million Lint compiler and the VSCode extension. Both the compiler and in-editor annotations are free to use forever. Our focus is to build a great developer tool, and we believe that the best way to build a great developer tool is to build it in the open.

To earn a living, we will charge the **Lint++** service at $20 per month for 100 lints. For more frequent users, we are still working on the details, but the idea is to charge based on the number of lints you translate to code. We believe this aligns our incentives with yours: we only make money when we make your app faster.

## The road to Million Lint 1.0

We are still in the very early days of experimentation! Million Lints focuses on solving unnecessary re-renders right now (but we understand this is [not a problem](https://react.dev/blog/2024/02/15/react-labs-what-we-have-been-working-on-february-2024) for long 🫡) and will move on to handling slow-downs arising from the React ecosystem: state managers, animations, bundle sizes, waterfalls, etc. Our eventual goal is to create a toolchain which keeps your whole web infrastructure fast, automatically - frontend to backend.

We would like to [invite you on this journey](https://million.dev/chat) with us to make the best possible web performance developer tool. Million Lint is our very first step. Try it out and let us know what pieces are missing!

## How can I help?

We are looking for talented frontend and pl/ml engineers to join us in the Bay Area.

At Million, we have a simple thesis for software performance – we can build tools that make *anyone* a performance expert. Developers should think only of shipping features and fixing bugs – not keeping their code fast. We plan to start with React, then extend to the broader frontend, backend, and other platforms.

If you feel like you missed the beginning of Million.js, get in on this. The fun of the beginning is how much you get to shape 🔜 [My email](mailto:aiden@million.dev)

<br />

<div className="flex flex-col items-center gap-4 pt-4">
  <Image src="/lint/team.png" alt="the Million team" height={450} width={600} />
  <small>
    _the Million team + [Ivan Akulov](https://twitter.com/iamakulov) fixing
    React performance one step at a time. Join us!_
  </small>
</div>
